﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Final
{
    class Parser
    {
        enum State { PROG0, S0, U0, I1, GoTo1, S1, E0, E1, B1, SL1 };

        int position; //The position into the token list

        State state;
        Symbol_Table s_table;
        Constant_Table c_table;

        List<Token> tokens;
        Stack<Token> stack;
        List<string> errors;
        Token current_token;

        Code_Generator code_Generator;

        public Parser(List<Token> tokens)
        {
            position = 0;
            current_token = null;
            state = State.PROG0;
            this.tokens = tokens;

            stack = new Stack<Token>();
            s_table = new Symbol_Table();
            c_table = new Constant_Table();

            code_Generator = new Code_Generator(s_table, c_table, stack);

            errors = new List<string>();

            errors.Add("Unknown Error");
            errors.Add("Program does not begin with 'BEGIN'");
            errors.Add("Statement does not begin properly");
            errors.Add("An identifier at the beginning of a statement must be followed immediately by : or ?");
            errors.Add("Go to statement requires identifier after go to");
            errors.Add("Expression begins improperly");
            errors.Add("Expression in improper context");
            errors.Add("Boolean expression used improperly in conditional statement or found in improper context");
            errors.Add("Statement occurs in improper context");
            errors.Add("Statement list in improper context");

            Parse();
        }

        private void Parse()
        {
            bool done = false;

            while (!done)
            {
                switch (state)
                {
                    case State.PROG0:
                        {
                            stack.Push(tokens.ElementAt<Token>(position));

                            Token_Operator to = stack.Peek() as Token_Operator;

                            if (to != null)
                            {
                                if (to.OPERATOR == '0') //Is it a BEGIN?
                                {
                                    state = State.S0;
                                    position++;
                                }
                                else
                                {
                                    throw (new SystemException(errors.ElementAt(1)));
                                }
                            }
                            else
                            {
                                throw (new SystemException(errors.ElementAt(1)));
                            }

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }
                    case State.S0:
                        {
                            if (stack.Peek().Type == Token_Type.OPERATOR)
                            {
                                Token_Operator to = stack.Peek() as Token_Operator;

                                if (to.OPERATOR == '3') //Is it an IF?
                                {
                                    state = State.E0;
                                    position++;
                                }
                                else
                                {
                                    stack.Pop();
                                    state = State.U0;
                                }
                            }
                            else
                            {
                                stack.Pop();
                                state = State.U0;
                            }

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }
                    case State.U0:
                        {
                            if (stack.Peek().Type == Token_Type.OPERATOR)
                            {
                                Token_Operator to = stack.Peek() as Token_Operator;

                                if (to.OPERATOR == '2')  //Is it a GOTO?
                                {
                                    state = State.GoTo1;
                                    position++;
                                }
                                else
                                {
                                    throw (new SystemException(errors.ElementAt(2)));
                                }
                            }
                            else if (stack.Peek().Type == Token_Type.VARIABLE)
                            {
                                state = State.I1;
                                position++;
                            }
                            else
                            {
                                throw (new SystemException(errors.ElementAt(2)));
                            }

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }
                    case State.I1:
                        {
                            if (stack.Peek().Type == Token_Type.OPERATOR)
                            {
                                Token_Operator to = stack.Peek() as Token_Operator;

                                if (to.OPERATOR == '←') //Assignment Operator!
                                {
                                    state = State.E0;
                                    position++;
                                }
                                else if (to.OPERATOR == ':')
                                {
                                    //EXEC 1
                                    state = State.S0;
                                }
                                else
                                {
                                    throw (new SystemException(errors.ElementAt(2)));
                                }
                            }
                            else
                            {
                                throw (new SystemException(errors.ElementAt(2)));
                            }

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }
                    case State.GoTo1:
                        {
                            break;
                        }
                    case State.S1:
                        {
                            Token temp = stack.Peek();
                            stack.Pop();

                            if (stack.Peek().Type == Token_Type.SPECIAL && (stack.Peek() as Token_Special).S_TYPE == Special_Type.S)
                            {
                                Token s = stack.Peek();
                                stack.Pop();

                                Token t = stack.Peek();

                                if (t.Type == Token_Type.SPECIAL && (t as Token_Special).S_TYPE == Special_Type.ICL)
                                {
                                    stack.Push(s);

                                    //EXEC3

                                    state = State.S1;
                                }
                                else if (t.Type == Token_Type.OPERATOR &&
                                    ((t as Token_Operator).OPERATOR == ':' || (t as Token_Operator).OPERATOR == ';')) //Might wanna double check this
                                {
                                    Token_Operator to = stack.Peek() as Token_Operator;
                                    stack.Pop();

                                    if (stack.Peek().Type == Token_Type.VARIABLE)
                                    {
                                        stack.Pop();

                                        stack.Push(new Token_Special(Special_Type.S));

                                        state = State.S1; //Redundent Maybe
                                    }
                                    else if (stack.Peek().Type == Token_Type.SPECIAL && (stack.Peek() as Token_Special).S_TYPE == Special_Type.SL)
                                    {
                                        state = State.SL1;
                                    }
                                    else
                                    {
                                        throw (new SystemException(errors.ElementAt(8)));
                                    }
                                }
                                else //Should execute if just S on top of stack
                                {
                                    //stack.Pop();

                                    stack.Push(new Token_Special(Special_Type.SL));

                                    state = State.SL1;
                                }
                            }
                            else
                            {
                                throw (new SystemException(errors.ElementAt(8)));
                            }

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }
                    case State.E0:
                        {
                            if (Is_I(stack.Peek()))
                            {
                                code_Generator.EXEC4();

                                state = State.E1;
                            }
                            else if (stack.Peek().Type == Token_Type.OPERATOR)
                            {
                                if ((stack.Peek() as Token_Operator).OPERATOR != '(')
                                {
                                    throw (new SystemException(errors.ElementAt(5)));
                                }
                                else
                                {
                                    if (code_Generator.AccEmpty())
                                    {
                                        //Check stuff
                                    }
                                }
                            }
                            else
                            {
                                throw (new SystemException(errors.ElementAt(5)));
                            }

                            position++;

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }
                    case State.E1:
                        {
                            Token t1 = stack.Peek(); //Store top of stack
                            stack.Pop();

                            if (stack.Peek().Type == Token_Type.PLACEHOLDER) //Make sure second item on stack is an E
                            {
                                Token_PlaceHolder t2 = stack.Peek() as Token_PlaceHolder; //Save E
                                stack.Pop();

                                Token_Operator t3 = stack.Peek() as Token_Operator; //Get new top of stack, should be operator
                                stack.Pop(); //Third pop

                                if (t1.Type == Token_Type.OPERATOR && (IsOperator((t1 as Token_Operator).OPERATOR) ||
                                    IsRelational((t1 as Token_Operator).OPERATOR)))
                                {
                                    stack.Push(t3);
                                    stack.Push(t2);
                                    stack.Push(t1);

                                    state = State.E0;

                                    position++;

                                    stack.Push(tokens.ElementAt<Token>(position));
                                }
                                else if (t1.Type == Token_Type.OPERATOR && (t1 as Token_Operator).OPERATOR == ')'
                                    && t3 != null && IsOperator(t3.OPERATOR) && stack.Peek().Type == Token_Type.PLACEHOLDER)
                                {
                                    stack.Push(t3);
                                    stack.Push(t2);
                                    stack.Push(t1); //?

                                    code_Generator.EXEC5();

                                    state = State.E1;

                                    position++;

                                    stack.Push(tokens.ElementAt<Token>(position));
                                }
                                else if (t3 != null && IsRelational(t3.OPERATOR) && stack.Peek().Type == Token_Type.PLACEHOLDER)
                                {
                                    stack.Push(t3);
                                    stack.Push(t2);
                                    stack.Push(t1); //?

                                    //Goes to B
                                    //EXEC 6
                                }
                                else if (t3 != null && t3.OPERATOR == '←' && Is_I(stack.Peek()))
                                {
                                    stack.Push(t3);
                                    stack.Push(t2);
                                    stack.Push(t1);

                                    code_Generator.EXEC7();

                                    state = State.S1;
                                }
                                else
                                {
                                    throw (new SystemException(errors.ElementAt(6)));
                                }
                            }
                            else
                            {
                                throw (new SystemException(errors.ElementAt(6)));
                            }

                            break;
                        }
                    case State.B1:
                        {
                            break;
                        }
                    case State.SL1:
                        {
                            Token t1 = stack.Peek();
                            stack.Pop();
                            Token t2 = stack.Peek();
                            stack.Pop();

                            if (t1.Type == Token_Type.OPERATOR && (t1 as Token_Operator).OPERATOR == '1')
                            {
                                if (t2.Type == Token_Type.SPECIAL && (t2 as Token_Special).S_TYPE == Special_Type.SL
                                    && stack.Peek().Type == Token_Type.OPERATOR &&
                                    (stack.Peek() as Token_Operator).OPERATOR == '0')
                                {
                                    code_Generator.EXEC9();

                                    done = true;
                                }
                                else
                                {
                                    throw(new SystemException(errors.ElementAt(9)));
                                }
                            }
                            else if (t1.Type == Token_Type.OPERATOR && (t1 as Token_Operator).OPERATOR == ';')
                            {
                                stack.Push(t2);
                                stack.Push(t1);

                                state = State.S0;
                            }
                            else
                            {
                                throw(new SystemException(errors.ElementAt(9)));
                            }

                            stack.Push(tokens.ElementAt<Token>(position));

                            break;
                        }

                }
            }
        }

        public Stack<Token> Stack
        {
            get { return stack; }
        }


        public Token Token
        {
            get { return current_token; }
        }

        //Is it relational ie >,<, or =
        private bool IsRelational(char c)
        {
            if (c == '>' || c == '<' || c == '=')
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        //Is it an operator *,/,+,-
        private bool IsOperator(char c)
        {
            if (c == '*' || c == '/' || c == '+' || c == '-')
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        //Could it be considered an I indentifier
        private bool Is_I(Token t)
        {
            if (t.Type == Token_Type.LITERAL || t.Type == Token_Type.VARIABLE)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
